/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
// pimgconv.cpp - Image conversion base class, used by GIF, BMP, and
//                JPG conversion classes.
//
// Author: Kenneth G. Maxwell
//
// Copyright (c) 1993-2000 Swell Software 
//              All Rights Reserved.
//
// Unauthorized redistribution of this source code, in whole or part,
// without the express written permission of Swell Software
// is strictly prohibited.
//
// Notes:
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/

#ifndef _PEG_IMAGE_CONVERT_
#define _PEG_IMAGE_CONVERT_

/*--------------------------------------------------------------------------*/
// PEG_PIC_FILE_MODE
//
// This defininition is CRITICAL to how the image conversion classes obtain
// the source (raw) image data. If PEG_PIC_FILE_MODE is turned on, the conversion
// classes use ANSI C file I/O to read the raw data directly. If PEG_PIC_FILE_MODE
// is turned off, the image conversion classes use a "callback" function
// supplied by the user to read image data.
/*--------------------------------------------------------------------------*/

#if defined(PEG_PIC_FILE_MODE) && defined(PEG_FILE_STDC)
#include "stdio.h"
#endif

#ifdef PEG_QUANT
class PegQuant;     // forward reference
#endif


/*--------------------------------------------------------------------------*/
// Conversion State- These values are combined in the converter state value,
//   which can be retrieved via the member function State()
/*--------------------------------------------------------------------------*/

#define PIC_IDLE           0x01  // waiting for input data
#define PIC_ERROR          0x02  // an error has occured
#define PIC_HEADER_KNOWN   0x04  // the header (width, height, type) is known
#define PIC_PALETTE_KNOWN  0x08  // the palette is known
#define PIC_ONE_CONV_DONE  0x10  // at least one conversion complete
#define PIC_COMPLETE       0x20  // all conversions are complete

/*--------------------------------------------------------------------------*/
// Conversion Modes- set by application to define how to convert
/*--------------------------------------------------------------------------*/

#define PIC_NO_CONVERT     0x00  // no inline conversion (custom palette mode)
#define PIC_INLINE_DITHER  0x01  // dither on the fly
#define PIC_INLINE_REMAP   0x02  // remap to best color on the fly
#define PIC_RGB_ORDER      0x04  // for 24bpp, use RGB instead of default BGR order
#define PIC_ALPHA_TRANS    0x08  // input has alpha, but no alpha in output
#define PIC_RGB_TRANS      0x10  // use RGB transparency
#define PIC_INDEX_TRANS    0x20  // for GIF, use index instead of RGB value
#define PIC_555_MODE       0x40  // for 16bpp, 5:6:5 or 5:5:5 mode?
#define PIC_INLINE_RESIZE  0x80

/*--------------------------------------------------------------------------*/
// Image Types- can be used to upcast PegImgConvert to 
// PegGifConvert, PegBmpConvert, PegJpgConvert, PegPngConvert
/*--------------------------------------------------------------------------*/
#define PIC_TYPE_BMP  1
#define PIC_TYPE_GIF  2
#define PIC_TYPE_JPG  3
#define PIC_TYPE_PNG  4


/*--------------------------------------------------------------------------*/
// Default conversion values- some of these can be changed at run time
/*--------------------------------------------------------------------------*/
#define    MAXCOLORMAPSIZE                256

#ifndef PIC_INPUT_BUF_SIZE
#define PIC_INPUT_BUF_SIZE          560
#endif

#ifndef PIC_INPUT_SLEEP_TIME
#define PIC_INPUT_SLEEP_TIME         10
#endif

#define FS_SCALE 1024

#define HI_COLORS           650         // actually 65536
#define TRUE_COLORS         1600        // actually 16 million
#define TRUE_ALPHA_COLORS   2400        // 32-bpp 8:8:8:8 format
#define YUVA_COLORS         3200        // actually  32bpp YUVA format

/*--------------------------------------------------------------------------*/
// Portability macros
/*--------------------------------------------------------------------------*/
#define BitSet(byte, bit)    (((byte) & (bit)) == (bit))
#define ReadOK(buffer,len)    (ConsumeImageInput(buffer, len) == len)
#define ReadBytes(buffer, len) ConsumeImageInput(buffer, len)

#ifndef PIC_SLEEP
#if defined(PEGSMX)
#define PIC_SLEEP  count(PIC_INPUT_SLEEP_TIME, ticks, INF);
#elif defined (PEGX)
#define PIC_SLEEP  tx_thread_sleep(PIC_INPUT_SLEEP_TIME);
#else
#define PIC_SLEEP
#endif
#endif

/*--------------------------------------------------------------------------*/

class PegPixel  {

    public:
        PegPixel(void) {Red = Green = Blue = Alpha = 0;}
        PegPixel(PEGLONG r, PEGLONG g, PEGLONG b);
        PEGBOOL operator != (const PegPixel &Pix) const;
        PEGBOOL operator == (const PegPixel &Pix) const;

        PEGLONG Red;
        PEGLONG Green;
        PEGLONG Blue;
        PEGUBYTE Alpha;
};

/*--------------------------------------------------------------------------*/
struct ConversionData {
    PegBitmap *pBitmap;
    PEGLONG DataSize;
};


/*--------------------------------------------------------------------------*/
struct PegImageInfo {
    PEGINT Width;
    PEGINT Height;
    PEGINT BitsPerPix;
};

/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/

class PegImageConvert
{
    public:

        PegImageConvert(PEGUINT Id);
        virtual ~PegImageConvert();

        PEGBOOL AssignImage(PegBitmap *pSrc);

       #ifdef PEG_QUANT
        void CountColors(PegQuant *pQuant);
       #endif

        PEGUINT ConsumeImageInput(PEGUBYTE *Buffer, PEGUINT Length);
        void DestroyImages(void);

        PEGBOOL DitherBitmap(void);
        PEGUINT GetBitmapCount(void) {return mImageCount;}
        PegBitmap *GetBitmapPointer(PEGUINT Index);
        PEGLONG GetDataSize(PEGUINT Index = 0);
        PEGCHAR *GetErrorString(void){return mpError;}

       #ifdef PEG_PIC_FILE_MODE
        virtual PEGBOOL GetImageInfo(PegFile *Src, PegImageInfo *info) = 0;
        static PEGUBYTE GetImageType(PegFile *Src);
       #else
           #if defined(PEG_PIC_MEMORY_MODE)
            static PEGUBYTE GetImageType(PEGUBYTE *pData);
           #endif
       #endif

        PEGUINT GetMode(void) {return mMode;}
        PEGUBYTE GetOutputBitsPerPix(void){return mOutBitsPerPix;}
        PegPixel GetPixelColor(PEGUBYTE *pStart, PEGUINT YPos,
                               PEGUINT Index);

        PEGUINT GetRowsConverted(void) {return mRowsConverted;}
        PEGUINT GetState(void) {return mState;}
        static PEGUINT GetStride(PEGUINT Width, PEGUINT Bits, PEGUINT HasAlpha = 0);
        static PEGUINT GetStride(PegBitmap *pMap);

        PegPixel GetTransColor()
        {
            return mTransColor;
        }

        PegPixel GetULCColor(void);

        void Id(PEGUINT Id) {mId = Id;}
        PEGUINT Id(void) {return mId;}
        PEGUBYTE ImageType(void) {return mImageType;}

       #if !defined(PEG_PIC_FILE_MODE)
        PEGUINT InputFreeSpace(void);
       #endif

        void KeepAlpha(PEGBOOL Keep)
        {
            mWantAlpha = Keep;

            if (mWantAlpha)
            {
                mMode &= ~PIC_ALPHA_TRANS;
            }

            if (Keep && mInputHasAlpha)
            {
                mAlphaIncluded = TRUE;
            }
        }

       #ifdef PEG_PIC_FILE_MODE
        virtual PEGBOOL ReadImage(PegFile *pFile, PEGINT Count = 100) = 0;
       #else
        virtual PEGBOOL ReadImage(PEGINT Count = 100) = 0;
       #endif

        PEGBOOL RemapBitmap(void);

       #ifdef PEG_CONVERT_RLECOMP
        PEGBOOL RleEncode(PEGBOOL Force = FALSE);
       #endif

       #if !defined(PEG_PIC_FILE_MODE)
        PEGUINT SendData(PEGUBYTE *Get, PEGUINT Size);
       #endif

        void SetGrayscale(PEGBOOL Gray)
        {
            mGrayscale = Gray;
        }

        void SetIdleCallback(PEGBOOL (*pFunc)(PEGUSHORT, PEGUSHORT,
                             PegImageConvert *));

       #if defined(PEG_PIC_MEMORY_MODE)
        void SetInputBuffer(PEGUBYTE *pGet, PEGULONG lSize);
       #endif

        void SetMode(PEGUINT Mode)
        {
            mMode = Mode;
        }

        void SetPalStart(PEGUINT Index) {mPalStart = Index;}
        void SetSystemPalette(PEGUBYTE *pPal, PEGUINT PalSize, 
                              PEGBOOL Fast = FALSE);

       #if defined(PEG_IMAGE_SCALING)
        void SetTargetSize(PEGINT Width, PEGINT Height)
        {
            mDestWidth = Width;
            mDestHeight = Height;
            mMode |= PIC_INLINE_RESIZE;
        }
       #endif

        void SetTransColor(PegPixel pPixel);
        void SetTransColor(PEGUBYTE Index);

       #if defined(PEG_HW_IMAGE_DECODER)
        virtual PEGBOOL HWConversionFail(void)
        {
            return TRUE;
         }
       #endif


    protected:

        PegBitmap *AddNewBitmapToList(PEGLONG Size);
        PEGUINT CountDuplicates(PEGUBYTE *Start, PEGUINT Index,
                                PEGUINT Width);
        void DitherBitmapRow(PEGUBYTE *pGet, PEGUBYTE *pPut, PEGUINT Width);
        void EndAllRemapping(void);
        void EndDither(void);
        void EndRemapping(PegBitmap *pMap);
        PEGUBYTE *GetLocalPalette(void){return mpLocalPalette;}

        static PEGLONG GetRawPix1(PEGUBYTE *pData, PEGUINT Index);
        static PEGLONG GetRawPix2(PEGUBYTE *pData, PEGUINT Index);
        static PEGLONG GetRawPix4(PEGUBYTE *pData, PEGUINT Index);
        static PEGLONG GetRawPix8Alpha(PEGUBYTE *pData, PEGUINT Index);
        static PEGLONG GetRawPix8(PEGUBYTE *pData, PEGUINT Index);
        static PEGLONG GetRawPix16Alpha(PEGUBYTE *pData, PEGUINT Index);
        static PEGLONG GetRawPix16(PEGUBYTE *pData, PEGUINT Index);
        static PEGLONG GetRawPix24Alpha(PEGUBYTE *pData, PEGUINT Index);
        static PEGLONG GetRawPix24(PEGUBYTE *pData, PEGUINT Index);

        PEGBOOL InitDither(PEGUINT cols);

        PEGBOOL InlineMode(void)
        {
            if (mMode & (PIC_INLINE_DITHER|PIC_INLINE_REMAP|
                PIC_INLINE_RESIZE))
            {
                return TRUE;
            }
            return FALSE;
        }

        PEGUBYTE LookupBestColor(PegPixel *pPixel);

        void pm_error(const PEGCHAR *);
        void pm_message(const PEGCHAR *);

        void ReadPixel1To4(PEGUBYTE *pGet, PEGUINT Index, PegPixel *pPut);
        void ReadPixel8Alpha(PEGUBYTE *pGet, PEGUINT Index, PegPixel *pPut);
        void ReadPixel8(PEGUBYTE *pGet, PEGUINT Index, PegPixel *pPut);
        void ReadPixel16_555(PEGUBYTE *pGet, PEGUINT Index, PegPixel *pPut);
        void ReadPixel16_555Alpha(PEGUBYTE *pGet, PEGUINT Index, PegPixel *pPut);
        void ReadPixel16_565(PEGUBYTE *pGet, PEGUINT Index, PegPixel *pPut);
        void ReadPixel16_565Alpha(PEGUBYTE *pGet, PEGUINT Index, PegPixel *pPut);
        void ReadPixel24Alpha(PEGUBYTE *pGet, PEGUINT Index, PegPixel *pPut);
        void ReadPixel24(PEGUBYTE *pGet, PEGUINT Index, PegPixel *pPut);

        void RemapBitmapRow(PEGUBYTE *pGet, PEGUBYTE *pPut, PEGUINT Width);

       #if defined(PEG_IMAGE_SCALING)
        void ResizeBitmapRow(PEGUINT SrcWidth);
       #endif

       #ifdef PEG_CONVERT_RLECOMP
        PEGUINT RleEncode(PEGUBYTE *pData, PEGUBYTE *pPut, PEGUINT Width);
       #endif

        void SendState(PEGUINT State);
        void SetInputStride(PEGUINT ImgWidth);
        void SetInternalBitsPix(PEGUINT BitsPix);
        void SetOutputColorDepth(PEGUINT BitsPix);
        void SetOutputStride(PEGUINT ImgWidth);
        PEGBOOL SetupInlineBuf(PEGUINT Width);

       #if defined(PEG_IMAGE_SCALING)
        PEGBOOL SetupInlineResizeBuf(void);
       #endif

        PEGBOOL TransMode(void)
        {
            if (mMode & (PIC_INDEX_TRANS|PIC_RGB_TRANS|PIC_ALPHA_TRANS))
            {
                return TRUE;
            }
            return FALSE;
        }

       void WritePixel1(PEGUBYTE *pLine,
                        PEGUINT PixIndex, PegPixel *pPixel);
       void WritePixel2(PEGUBYTE *pLine,
                        PEGUINT PixIndex, PegPixel *pPixel);
       void WritePixel4(PEGUBYTE *pLine,
                        PEGUINT PixIndex, PegPixel *pPixel);
       void WritePixel8_332(PEGUBYTE *pLine,
                        PEGUINT PixIndex, PegPixel *pPixel);
       void WritePixel8(PEGUBYTE *pLine,
                        PEGUINT PixIndex, PegPixel *pPixel);
       void WritePixel16_555_Alpha(PEGUBYTE *pLine,
                        PEGUINT PixIndex, PegPixel *pPixel);
       void WritePixel16_555(PEGUBYTE *pLine,
                        PEGUINT PixIndex, PegPixel *pPixel);
       void WritePixel16_565_Alpha(PEGUBYTE *pLine,
                        PEGUINT PixIndex, PegPixel *pPixel);
       void WritePixel16_565(PEGUBYTE *pLine,
                        PEGUINT PixIndex, PegPixel *pPixel);
       void WritePixel24_RGB_Alpha(PEGUBYTE *pLine,
                        PEGUINT PixIndex, PegPixel *pPixel);
       void WritePixel32_RGB_NoAlpha(PEGUBYTE *pLine,
                        PEGUINT PixIndex, PegPixel *pPixel);
       void WritePixel24_RGB(PEGUBYTE *pLine,
                        PEGUINT PixIndex, PegPixel *pPixel);
       void WritePixel24_BGR_Alpha(PEGUBYTE *pLine,
                        PEGUINT PixIndex, PegPixel *pPixel);
       void WritePixel32_BGR_NoAlpha(PEGUBYTE *pLine,
                        PEGUINT PixIndex, PegPixel *pPixel);
       void WritePixel24_BGR(PEGUBYTE *pLine,
                        PEGUINT PixIndex, PegPixel *pPixel);

       #if defined(PEG_IMAGE_SCALING)
        static void WriteRaw1(PEGUBYTE *pData, PEGUINT Index,
                              PEGLONG Val);
        static void WriteRaw2(PEGUBYTE *pData, PEGUINT Index,
                              PEGLONG Val);
        static void WriteRaw4(PEGUBYTE *pData, PEGUINT Index,
                              PEGLONG Val);
        static void WriteRaw8Alpha(PEGUBYTE *pData, PEGUINT Index,
                              PEGLONG Val);
        static void WriteRaw8(PEGUBYTE *pData, PEGUINT Index,
                              PEGLONG Val);
        static void WriteRaw16Alpha(PEGUBYTE *pData, PEGUINT Index,
                              PEGLONG Val);
        static void WriteRaw16(PEGUBYTE *pData, PEGUINT Index,
                              PEGLONG Val);
        static void WriteRaw24Alpha(PEGUBYTE *pData, PEGUINT Index,
                              PEGLONG Val);
        static void WriteRaw24(PEGUBYTE *pData, PEGUINT Index,
                              PEGLONG Val);
       #endif

        void WriteTrans1to4(PEGUBYTE *pPut, PEGUINT PixIndex);
        void WriteTrans8_332(PEGUBYTE *pPut, PEGUINT PixIndex);
        void WriteTrans8(PEGUBYTE *pPut, PEGUINT PixIndex);
        void WriteTrans16(PEGUBYTE *pPut, PEGUINT PixIndex);
        void WriteTrans24RGB(PEGUBYTE *pPut, PEGUINT PixIndex);
        void WriteTrans24BGR(PEGUBYTE *pPut, PEGUINT PixIndex);
        void WriteTrans32RGB(PEGUBYTE *pPut, PEGUINT PixIndex);
        void WriteTrans32BGR(PEGUBYTE *pPut, PEGUINT PixIndex);


        // Indirect function pointers

        PEGBOOL  (*mpCallBack)   (PEGUSHORT, PEGUSHORT, PegImageConvert *);
        PEGLONG  (*mpReadRaw)    (PEGUBYTE *, PEGUINT);

       #if defined(PEG_IMAGE_SCALING)
        void     (*mpWriteRaw)   (PEGUBYTE *, PEGUINT, PEGLONG);
       #endif

        void (PegImageConvert::*mpReadPixel)(PEGUBYTE *, PEGUINT, PegPixel *);
        void (PegImageConvert::*mpWritePixel)(PEGUBYTE *, PEGUINT, PegPixel *);
        void (PegImageConvert::*mpWriteTrans)(PEGUBYTE *, PEGUINT);


        PegPixel mTransColor;
        PegPixel mPixelWritten;
        ConversionData *mpConversionData;

       #ifdef PEG_PIC_FILE_MODE
        PegFile *mFile;
	   
       #elif defined(PEG_PIC_MEMORY_MODE)

        PEGUBYTE *mpInBufferStart;
        PEGUBYTE *mpInBufferPtr;
        PEGULONG mInBufferSize;
       #endif

        PEGLONG *mpThisRerr;
        PEGLONG *mpNextRerr;
        PEGLONG *mpThisGerr;
        PEGLONG *mpNextGerr;
        PEGLONG *mpThisBerr;
        PEGLONG *mpNextBerr;
        PEGUBYTE *mpSystemPalette;
        PEGUBYTE *mpLocalPalette;
        PEGUBYTE *mpInlineReadBuf;      // storage for inline color mapping

       #ifndef PEG_PIC_FILE_MODE
        PEGUBYTE *mpInBuf;
        PEGUBYTE *mpPutInData;
        PEGUBYTE *mpGetInData;
        PEGUBYTE *mpInBufStop;
       #endif

       #if defined(PEG_IMAGE_SCALING)
        PEGUBYTE *mpInlineResizeBuf;    // storage for inline resize
       #endif

        PEGCHAR mpError[82];

        PEGLONG mDataSize;
        PEGUINT mOutBitsPerPix;
        PEGUINT mInternalBitsPerPix;
        PEGUINT mSysPalSize;
        PEGUINT mLocalPalSize;
        PEGUINT mInputStride;
        PEGUINT mOutputStride;
        PEGUINT mRowsConverted;
        PEGUINT mImageCount;
        PEGUINT mPalStart;
        PEGUINT mState;
        PEGUINT mMode;
        PEGUINT mId;
       
       #if defined(PEG_IMAGE_SCALING)
        PEGINT   mDestWidth;          // for inline re-size on the fly
        PEGINT   mDestHeight;
       #endif

        PEGBOOL mFSDir;
        PEGBOOL mDithering;
        PEGBOOL mGrayscale;
        PEGBOOL mWantAlpha;        // Include alpha channel in output data
        PEGBOOL mInputHasAlpha;       // Input file includes alpha channel
        PEGBOOL mAlphaIncluded;
        PEGUBYTE mImageType;

};


#endif   // PEG_IMAGE_CONVERT

// end of file










